#include "base/core/csv_parser.h"
#include "base/core/com_define.h"
#include "base/core/Utf8AndWideChar.h"
#include "base/core/char_operation.h"
#include "base/core/os/file.h"
#include "base/core/os/debug.h"
#include <string.h>

static const char * EMPTY_STRING = "";

#define g_StrCpyLen(dest, src, size)    \
{                                       \
    SafeCopyString(dest, size, src);           \
    dest[size - 1] = '\0';              \
}

inline size_t _HashString(const char* str) 
{
    size_t hash_num = 0;
    for (; *str; ++str) 
	{
        hash_num = 31 * hash_num + *str;
    }
    return hash_num;
}

int _SafeCopyString(char* buffer, size_t buffer_size, const char* cpy_string) 
{
    MMO_ASSERT(cpy_string);
    int ret = 1;
    size_t length = strlen(cpy_string);
    MMOLOG_PROCESS_ERROR(length + 1 <= buffer_size);
    memcpy(buffer, cpy_string, length + 1);
    ret = 0;
Exit0:
    return ret;
}

int CsvParser::FindRow(const char* row_name) 
{
    int ret_line = -1;
    size_t string_id = _HashString(row_name);
    auto iter = m_row_id_table.find(string_id);
	if (iter != m_row_id_table.end())
	{
		ret_line = iter->second;
	}
    return ret_line;
}

int CsvParser::FindColumn(const char* column_name) 
{
    int ret_column = -1;
    size_t string_id = _HashString(column_name);
    auto iter = m_col_id_table.find(string_id);
    if (iter != m_col_id_table.end())
	{ 
		ret_column = iter->second;
	}
    return ret_column;
}

int CsvParser::GetString(int row_idx, int column_idx, const char* default_str, char* ret_str, unsigned int buf_max_size) 
{
    int ret = 1;
    const char * cells = NULL;
    MMOLOG_PROCESS_ERROR(ret_str);
    cells = GetCell(row_idx, column_idx);
    MMO_PROCESS_ERROR(cells);
    MMO_PROCESS_ERROR(*cells);
    MMOLOG_PROCESS_ERROR(_SafeCopyString(ret_str, buf_max_size, cells) == 0);
    ret = 0;
Exit0:
    if (ret) 
	{
        if (ret_str && default_str) 
		{
            g_StrCpyLen(ret_str, default_str, buf_max_size);
        }
    }
    return ret;
}

int CsvParser::GetString(const char* row_name, const char* column_name, const char* default_str, char* ret_str, unsigned int buf_max_size) 
{
    int row_idx = FindRow(row_name);
    int column_idx = FindColumn(column_name);
    return GetString(row_idx, column_idx, default_str, ret_str, buf_max_size);
}

int CsvParser::GetString(int row_idx, const char* column_name, const char* default_str, char* ret_str, unsigned int buf_max_size) 
{
    int column_idx = FindColumn(column_name);
    return GetString(row_idx, column_idx, default_str, ret_str, buf_max_size);
}

int CsvParser::GetInteger(int row_idx, int column_idx, int default_num, int* ret_num) 
{
    int ret = 1;
    char *str_end = NULL;
    const char * cells = NULL;
    MMOLOG_PROCESS_ERROR(ret_num);
    cells = GetCell(row_idx, column_idx);
    MMO_PROCESS_ERROR(cells);
    MMO_PROCESS_ERROR(*cells);
    *ret_num = strtol(cells, &str_end, 0);
    ret = 0;
Exit0:
    if (ret) 
	{
        if (ret_num) 
		{
            *ret_num = default_num;
        }
    }
    return ret;
}

int CsvParser::GetInteger(const char* row_name, const char* column_name, int default_num, int* ret_num) 
{
    int row_idx = FindRow(row_name);
    int column_idx = FindColumn(column_name);
    return GetInteger(row_idx, column_idx, default_num, ret_num);
}

int CsvParser::GetInteger(int row_idx, const char* column_name, int default_num, int* ret_num) 
{
    int column_idx = FindColumn(column_name);
    return GetInteger(row_idx, column_idx, default_num, ret_num);
}

int CsvParser::GetFloat(int row_idx, int column_idx, float defalut_num, float* ret_num) 
{
    int ret = 1;
    char *str_end = NULL;
    const char * cells = NULL;
    MMOLOG_PROCESS_ERROR(ret_num);
    cells = GetCell(row_idx, column_idx);
    MMOLOG_PROCESS_ERROR(cells);
    MMO_PROCESS_ERROR(*cells);
    *ret_num = (float)strtod(cells, &str_end);
    ret = 0;
Exit0:
    if (ret) 
	{
        if (ret_num) 
		{
            *ret_num = defalut_num;
        }
    }
    return ret;
}

int CsvParser::GetFloat(const char* row_name, const char* column_name, float default_num, float* ret_num) 
{
    int row_idx = FindRow(row_name);
    int column_idx = FindColumn(column_name);
    return GetFloat(row_idx, column_idx, default_num, ret_num);
}

int CsvParser::GetFloat(int row_idx, const char* column_name, float default_num, float* ret_num) 
{
    int column_idx = FindColumn(column_name);
    return GetFloat(row_idx, column_idx, default_num, ret_num);
}

CsvParser::CsvParser() 
{
    m_row_count = 0;
    m_column_count = 0;
	m_column_name_idx = 1;
    m_buffer = NULL;
    m_buffer_end = NULL;
    m_table_cells = NULL;
}
CsvParser::CsvParser(int colname_idx)
{
	m_row_count = 0;
	m_column_count = 0;
	m_column_name_idx = colname_idx;
	m_buffer = NULL;
	m_buffer_end = NULL;
	m_table_cells = NULL;
}

CsvParser::~CsvParser() 
{
    DestroyTableIndex();
    DestroyTableCells();
    MMO_DELETE_ARRAY(m_buffer);
}

int CsvParser::LoadFromBuffer(const char* buffer, size_t size) 
{
    int ret = 1;
    int create_buffer_flag = 0;
    int create_table_cell_flag  = 0;
    int create_table_index_flag = 0;

    m_buffer = new char[size + 1];
    MMOLOG_PROCESS_ERROR(m_buffer);
    create_buffer_flag = 1;

    memcpy(m_buffer, buffer, size);
    m_buffer[size] = '\0';
    m_buffer_end = m_buffer + size;

    ret = CreateTableCells();
    MMOLOG_PROCESS_ERROR(ret == 0);
    create_table_cell_flag = 1;

    ret = CreateTableIndex();
    MMOLOG_PROCESS_ERROR(ret == 0);
    create_table_index_flag = 1;

    ret = 0;
Exit0:
    if (ret) 
	{
        if (create_table_index_flag) 
		{
            DestroyTableIndex();
            create_table_index_flag = 0;
        }
        if (create_table_cell_flag) 
		{
            DestroyTableCells();
            create_table_cell_flag = 0;
        }
        if (create_buffer_flag) 
		{
            MMO_DELETE_ARRAY(m_buffer);
            create_buffer_flag = 0;
        }
    }
    return ret;
}

int CsvParser::CreateTableCells() 
{
    int ret = 1;
    size_t row_index = 0;
    size_t cells_count = 0;
    const char ** current_cell = NULL;
    char *  cell_b = NULL;
    char *  current_pos = NULL;

    ret = GetMaxRowCountAndMaxColumnCount(m_row_count, m_column_count);
    MMOLOG_PROCESS_ERROR(ret == 0);
    row_index = 0;
    cells_count = m_row_count * m_column_count;
    m_table_cells = new const char*[cells_count];
    MMOLOG_PROCESS_ERROR(m_table_cells);
    for (size_t i = 0; i < cells_count; ++i) 
	{
        m_table_cells[i] = EMPTY_STRING;
    }
    current_cell = m_table_cells;
    cell_b = m_buffer;
    current_pos = m_buffer;
    for (current_pos = m_buffer; current_pos < m_buffer_end; ++current_pos) 
	{
        char value = *current_pos;
        if (value == ',') 
		{
            *current_pos = '\0';
            *current_cell++ = cell_b;
            cell_b = current_pos + 1;
            continue;
        }
        if (value == '\r') 
		{
            *current_pos = '\0';
            continue;
        }
        if (value == '\n') 
		{
            *current_pos = '\0';
            *current_cell++ = cell_b;
            cell_b = current_pos + 1;
            ++row_index;
            current_cell = m_table_cells + row_index * m_column_count;
            continue;
        }
    }
    if (current_pos > cell_b) 
	{
        *current_cell++ = cell_b;
    }
    ret = 0;
Exit0:
    if (ret) 
	{
        MMO_DELETE_ARRAY(m_table_cells);
        m_row_count = 0;
        m_column_count = 0;
    }
    return ret;
}

int CsvParser::DestroyTableCells() 
{
    MMO_DELETE_ARRAY(m_table_cells);
    m_row_count = 0;
    m_column_count = 0;
    return 0;
}

int CsvParser::CreateTableIndex() 
{
    MMO_ASSERT(m_col_id_table.size() == 0);
	MMO_ASSERT(m_column_name_idx >= 1);
    if (m_row_count >= 1) 
	{
        for (int col_idx = 1; col_idx <= m_column_count; ++col_idx) 
		{
            //const char * str = FastGetCell(1, col_idx);
			const char * str = FastGetCell(m_column_name_idx, col_idx);
            size_t string_id = _HashString(str);
            m_col_id_table.insert(std::make_pair(string_id, col_idx));
        }
    }
    MMO_ASSERT(m_row_id_table.size() == 0);
    if (m_column_count >= 1) 
	{
        for (int row_idx = 1; row_idx <= m_row_count; ++row_idx) 
		{
            const char * str = GetCell(row_idx, 1);
            size_t string_id = _HashString(str);
            m_row_id_table.insert(std::make_pair(string_id, row_idx));
        }
    }
    return 0;
}

int CsvParser::DestroyTableIndex() 
{
    m_col_id_table.clear();
    m_row_id_table.clear();
    return 0;
}

int CsvParser::LoadFromFile(const char* path) 
{
    const unsigned char szUtf8Bom[3] = { 0xEF, 0xBB, 0xBF };
    int ret = 1;
    char* buffer = nullptr;
    File* file = nullptr;
    size_t size = 0;
    int ignoreLen = 0;

    file = FileOpen(path, "rb");
    MMOLOG_PROCESS_ERROR(file != nullptr);
    size = file->GetSize();
    buffer = new char[file->GetSize()];
    MMOLOG_PROCESS_ERROR(buffer != nullptr);
    file->Read(buffer, file->GetSize());
    FileClose(file);
    file = nullptr;
    if (size > sizeof(szUtf8Bom))
	{
        int nRetVal = memcmp(buffer, szUtf8Bom, sizeof(szUtf8Bom));
        if (nRetVal == 0){
            ignoreLen = 3;
        }
    }
    MMOLOG_PROCESS_ERROR(LoadFromBuffer(buffer + ignoreLen, size - ignoreLen) == 0);
    ret = 0;
Exit0:
    if (file != nullptr) 
	{
        FileClose(file);
        file = nullptr;
    }
    MMO_DELETE_ARRAY(buffer);
    return ret;
}

const char* CsvParser::GetCell(int row_idx, int column_idx) 
{
    --row_idx;
    --column_idx;
    const char *result = NULL;
    MMOLOG_PROCESS_ERROR(row_idx >= 0 && row_idx < m_row_count);
    MMO_PROCESS_ERROR(column_idx >= 0 && column_idx < m_column_count);
    result = m_table_cells[row_idx * m_column_count + column_idx];
Exit0:
    return result;
}

const char * CsvParser::FastGetCell(int row_idx, int column_idx) 
{
    MMO_ASSERT(row_idx > 0);
    MMO_ASSERT(column_idx > 0);
    return m_table_cells[(row_idx - 1) * m_column_count + column_idx - 1];
}

int CsvParser::GetMaxRowCountAndMaxColumnCount(int& ret_row_count, int& ret_column_count) 
{
    int row_count = 0;
    int column_count = 0;
    int max_column_count = 0;
    for (char * offset = m_buffer; offset < m_buffer_end; ++offset) 
	{
        unsigned char value = *offset;
        if (value == ',') 
		{
            ++column_count;
            continue;
        }
        if (value == '\n') 
		{
            ++column_count;
            ++row_count;
            max_column_count = max_column_count > column_count ? max_column_count : column_count;
            column_count = 0;
            continue;
        }
    }
    if (m_buffer < m_buffer_end && m_buffer_end[-1] != '\n')
	{
        ++row_count;
    }
    ret_row_count = row_count;
    ret_column_count = max_column_count;
    return 0;
}
